iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 11
1
Modern Web

循序漸進學習 Javascript 測試系列 第 11

Day11 實戰 Jest 配置:搞定前端測試配置,了解 Jest 如何在 Node 環境測試 Browser code

  • 分享至 

  • xImage
  •  

確保測試時讓 Babel 編譯 modules

在前端專案裡,我們通常會使用 Webpac 搭配 Babel Loader 來編譯瀏覽器不相容的語法,Webpac 預設是可以辨識 ESModule 的,而且 tree shaking 功能可以幫我們排除沒有實際使用的 module,所以我們把 compile importexport 語法的工作交給 Webpac。因此我們在 babelrc.js 中設定 {modules: false} ,讓 @babel/preset-env 不要 compile modules:

babelrc.js

module.exports = {
  presets: [
    ['@babel/preset-env', {modules: false}],
    '@babel/preset-react',
    [
    ...

這時候,我們為專案加入測試,假設有一個 __tests__/utils.js 檔案,要測試 getFormattedValue 這個 function:

tests/utils.js

import {getFormattedValue} from '../utils'

test('formats the value', () => {
  expect(getFormattedValue('1234.0')).toBe('1,234.0')
})

現在 npm run test 會拋出錯誤: syntax error -- Unexpected Token {. 。這個錯誤是因為 Jest 是在 node 環境執行的,但 node 並不認得 import 語法。

另一部分,如果我們的專案中已有 babel 配置,Jest 預設會自動抓取我們的 .babelrc **設定,跑測試時自動使用這份配置。**但因為我們要讓 Webpac 來處理 module,關閉了讓 @babel/preset-env compile modules 的功能,這就是這個錯誤被拋出的原因。

如果想要繼續讓 Webpac 處理 modules,兼顧 Webpac 的 tree shaking 功能,又同時能讓 Jest 順利跑測試,我們可以透過環境變數來判斷:測試環境時 babel compile modules,其他情況則不 compile。Jest 測試時會自動爲我們設置一個環境變數 NODE_ENV =test ,因此我們可以在 babelrc.js 裡加上:

babelrc.js

const isTest = String(process.env.NODE_ENV) === 'test'

module.exports = {
  presets: [
    ['@babel/preset-env', {modules: isTest ? 'commonjs' : false}],
    '@babel/preset-react',
    [
    ...

這個 babel 配置代表:跑測試時,babel 幫我們 compile modules 為 commonjs,確保在 node 環境可以運行,其他時候,我們一樣把 modules 交給 Webpac 來處理。

Tips:Jest 會自動抓取 .babelrc 設定。

配置 Test Environment,讓 Jest 辨別測試 Node 或 Browser code

我們都知道,在 Node 環境裡並沒有 windowdocument object,但我們現在直接在測試檔案中 console.log(window) 然後 npm run test 看看,結果沒有拋出錯誤。這是因為 Jest 預設內建了 jsdom ,透過 jsdom 模擬 browser 環境的物件,所以即使沒有這些 object 的 Node 環境,Jest 依然能順利測試 Browser code。

Jest 可以讓我們配置專案要測試的是在哪個環境運行的程式碼,可以在 CLI 加上 --env tag。假如我們要測試的 code 是 node 環境的程式碼:

npm run test -- --env=node

這時候 Jest 會排除 jsdom 執行測試,剛剛加上的 console.log(window) 就會拋出 ReferenceError: window is not defined 錯誤。

如果不想每次都在 CLI 加上 config,也可以在根目錄新增一個 jest.config.js 配置檔,設置 testEnvironment'jest-environment-node'

jest.config.js

module.exports = {
  testEnvironment: 'jest-environment-node'
}

假設是前端專案,會包含很多 Browser code,可以改回 'jest-environment-jsdom'

jest.config.js

module.exports = {
  testEnvironment: 'jest-environment-jsdom'
}

其實, jest-environment-jsdomjest-environment-node 實際上是 node module,我們可以在 node_modules 資料夾裡找到它們。它們是 Jest 的內建依賴套件,在安裝 Jest 的時候,也會一併安裝。

Tips:Jest 預設已內建 jsdom,透過 jsdom,Jest 可以模擬 browser 環境執行測試

使用 moduleNameMapper,讓 Jest 可測試內含 import css 檔案的 component

假設我們現在要測試前端 React 專案的一個 component: AutoScalingText ,這個 component 裡 import 了 css module:

auto-scaling-text.js

...
import styles from './auto-scaling-text.module.css'

class AutoScalingText extends React.Component {
  ...
}

export default AutoScalingText

這時候執行測試,可能會在 import styles from './auto-scaling-text.module.css 這裡拋出 syntax error 。原因是因為:Jest 試圖將這個檔案當作 common JS module 來 require,但實際上並不是,這是一個 css 檔案。

Jest 建議我們可以使用 moduleNameMapper 將原本要引入的檔案,map 到另一個 mock module,Jest 會引入這個 mock。

現在在 jest.config.js 新增一個 moduleNameMapper object,定義所有符合條件的檔名(在這裡就是所有 CSS 檔),都去拿另一個 module 取代:

jest.config.js

module.exports = {
  testEnvironment: 'jest-environment-jsdom',
  moduleNameMapper: {
    '\\.css$': require.resolve('./test/style-mock.js'),
  },
}

上面代表我們拿 './test/style-mock.js' 這個 module 取代原本的 css file,因此我們需要有一個 test/style-mock.js 檔案,裡面 export 出 style 的 mock:

test/style-mock.js

module.exports = {}

現在,Jest 跑測試時,import styles from './auto-scaling-text.module.css 拿到的 style 會是一個空的 object {}

通常在 Webpac 包裹專案時,之所以 import css files 能正常執行,是因為我們在 Webpac 配置了 CSS-loaderstyle-loader 幫忙處理引入 css 檔案。但在測試時,我們也需要讓 Jest 知道如何 import 這些 css 檔案。


上一篇
Day10 實戰 Jest 配置:準備篇
下一篇
Day12 實戰 Jest 配置:Jest Snapshots 幫助撰寫跟維護 assertions
系列文
循序漸進學習 Javascript 測試30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言